package com.imooc.bilibili.util;

import com.github.tobato.fastdfs.domain.fdfs.FileInfo;
import com.github.tobato.fastdfs.domain.fdfs.MetaData;
import com.github.tobato.fastdfs.domain.fdfs.StorePath;
import com.github.tobato.fastdfs.service.AppendFileStorageClient;
import com.github.tobato.fastdfs.service.FastFileStorageClient;
import com.imooc.bilibili.domain.exception.ConditionException;
import com.mysql.cj.util.StringUtils;
import io.netty.util.internal.StringUtil;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.*;

/**
 * ProjectName imooc-bilibili
 *
 * @author xieyucan
 * <br>CreateDate 2022/10/25 16:21
 */
@Component
public class FastDFSUtil {

    private static final String DEFAULT_GROUP = "group1";

    @Resource
    private FastFileStorageClient fastFileStorageClient;

    @Resource
    private AppendFileStorageClient appendFileStorageClient;



    @Resource
    private RedisTemplate redisTemplate;

    /**
     * 获取文件后缀名
     * @param file
     * @return
     */
    public String getFileType(MultipartFile file){
        if(file==null){
            throw new ConditionException("非法文件");
        }
        String filename = file.getOriginalFilename();
        int index = filename.lastIndexOf(".");
        return filename.substring(index+1);
    }
    //文件上传
    public String uploadCommonFile(MultipartFile file) throws IOException {
        Set<MetaData> metaDataSet=new HashSet<>();
        String fileType = getFileType(file);
        InputStream inputStream = file.getInputStream();
        StorePath storePath = fastFileStorageClient.uploadFile(inputStream, file.getSize(), fileType, metaDataSet);
        return storePath.getPath();
    }
    public String uploadCommonFile(MultipartFile file,String fileType) throws IOException
    {
        Set<MetaData> metaDataSet = new HashSet<>();
        StorePath storePath = fastFileStorageClient.uploadFile(DEFAULT_GROUP, file.getInputStream(), file.getSize(), fileType);
        return storePath.getPath();
    }

    /**
     * 分片上传中的第一个上传文件
     * @param file
     * @return
     * @throws IOException
     */
    public String uploadAppendFile(MultipartFile file) throws IOException {
        String fileType = getFileType(file);
        StorePath storePath = appendFileStorageClient.uploadAppenderFile(DEFAULT_GROUP, file.getInputStream(), file.getSize(), fileType);
        return storePath.getPath();
    }

    private static final String PATH_KEY = "path-key:";

    private static final String UPLOADED_SIZE_KEY = "uploaded-size-key:";

    private static final String UPLOADED_NO_KEY = "uploaded-no-key:";

    private static final int SLICE_SIZE = 1024 * 1024 * 2;
    /**
     * 分片上传文件之后的追加文件
     * @param file
     * @param filePath
     * @param offset
     * @throws IOException
     */
    public void modifyAppendFile(MultipartFile file,String filePath,long offset) throws IOException {
        appendFileStorageClient.modifyFile(DEFAULT_GROUP,filePath,file.getInputStream(),file.getSize(),offset);
    }
    public String uploadFileBySlices(MultipartFile file,String fileMd5,Integer sliceNo,Integer totalSliceNo) throws IOException {
        //判断参数问题
        if(file==null||sliceNo==null||totalSliceNo==null)
        {
            throw new ConditionException("参数异常");
        }
        //设置键值对
        String pathKey=PATH_KEY+fileMd5;
        String uploadedSizeKey=UPLOADED_SIZE_KEY+fileMd5;
        String uploadedNoKey=UPLOADED_NO_KEY+fileMd5;
        String uploadedSizeKeyStr=(String) redisTemplate.opsForValue().get(uploadedSizeKey);
        Long uploadSize=0L;
        if(uploadedSizeKeyStr!=null)
        {
            uploadSize=Long.valueOf(uploadedSizeKeyStr);
        }
        //判断是否是第一次上传文件
        if(sliceNo==1){
            String filePath = this.uploadAppendFile(file);
            if(StringUtils.isNullOrEmpty(filePath)){
                throw new ConditionException("文件上传失败");
            }
            redisTemplate.opsForValue().set(pathKey,filePath);
            redisTemplate.opsForValue().set(uploadedNoKey,"1");
        } else {
            String filePath =(String) redisTemplate.opsForValue().get(pathKey);
            if (StringUtils.isNullOrEmpty(filePath)){
                throw new ConditionException("文件上传失败");
            }
            this.modifyAppendFile(file,filePath,uploadSize);
            redisTemplate.opsForValue().increment(uploadedNoKey);
        }
        //统计分片的大小
        uploadSize+=file.getSize();
        redisTemplate.opsForValue().set(uploadedSizeKey,uploadSize);
        String uploadNoStr=(String) redisTemplate.opsForValue().get(uploadedNoKey);
        Long uploadNo=Long.valueOf(uploadNoStr);
        String resultPath="";
        //获取到地址之后就清除redis中的数据
        if(totalSliceNo.equals(uploadNo))
        {
            resultPath=(String) redisTemplate.opsForValue().get(pathKey);
            List<String> list = Arrays.asList(pathKey, uploadedSizeKey, uploadedNoKey);
            redisTemplate.delete(list);
        }
        //返回路径信息
        return resultPath;
    }

    public void convertFileToSlices(MultipartFile multipartFile) throws IOException {
        String fileType = getFileType(multipartFile);
        File file = multipartFileToFile(multipartFile);
        long fileLength = file.length();
        int count=1;
        for(int i=0;i<fileLength;i+=SLICE_SIZE)
        {
            RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r");
            randomAccessFile.seek(i);
            byte[]bytes=new byte[SLICE_SIZE];
            int len = randomAccessFile.read(bytes);
            String path="/user/"+count+"."+fileType;
            File slice = new File(path);
            FileOutputStream fileOutputStream = new FileOutputStream(slice);
            fileOutputStream.write(bytes,0,len);
            fileOutputStream.close();
            randomAccessFile.close();
            count++;
        }
        file.delete();
    }

    public File multipartFileToFile(MultipartFile multipartFile) throws IOException {
        String filename = multipartFile.getOriginalFilename();
        String[] split = filename.split("\\.");
        File file = File.createTempFile(split[0], "." + split[1]);
        multipartFile.transferTo(file);
        return file;
    }

    private final String httpFdfsStorageAddr="abc";

    public void viewVideoOnlineBySlices(HttpServletRequest request, HttpServletResponse response,String path) throws Exception {
        FileInfo fileInfo = fastFileStorageClient.queryFileInfo(DEFAULT_GROUP, path);
        long totalFileSize = fileInfo.getFileSize();
        String url=httpFdfsStorageAddr+path;
        Enumeration<String> headerNames = request.getHeaderNames();
        Map<String,Object> map=new HashMap<>();
        while(headerNames.hasMoreElements())
        {
            String element = headerNames.nextElement();
            map.put(element,request.getHeader(element));
        }
        String rangeStr = request.getHeader("Range");
        String[] range;
        if(StringUtil.isNullOrEmpty(rangeStr)){
            rangeStr = "bytes=0-" + (totalFileSize-1);
        }
        range = rangeStr.split("bytes=|-");
        long begin = 0;
        if(range.length >= 2){
            begin = Long.parseLong(range[1]);
        }
        long end = totalFileSize-1;
        if(range.length >= 3){
            end = Long.parseLong(range[2]);
        }
        long len = (end - begin) + 1;
        String contentRange = "bytes " + begin + "-" + end + "/" + totalFileSize;
        response.setHeader("Content-Range", contentRange);
        response.setHeader("Accept-Ranges", "bytes");
        response.setHeader("Content-Type", "video/mp4");
        response.setContentLength((int)len);
        response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
        HttpUtil.get(url, map, response);
    }
}
